#!/usr/bin/perl

=head1 NAME

AFX::Config - Audit Config File (ACF) Management Script.

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2010 Mikhail Volkov

This file is part of AuditFX.

    AuditFX is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    AuditFX is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with AuditFX.  If not, see <http://www.gnu.org/licenses/>.
=cut

##############################################################################
# Globals
#

use lib 'lib';
use strict;
use warnings;
use File::Copy;
use Config;

use AFX::ACF;
use AFX::ASF;

## Execute main script
config();

##############################################################################
#
# config()
#
# Configure
#
# Command arguments expected are:
# - site_mach - Site's machine

sub config {

## Print config usage message
    print config_usage();

## Check thread suport
    if ( !$Config{usethreads} ) {

## Exit with status
        die "Threads aren't supported! Exit.\n";
    }

## Update libraries
    my $updated = lib_update();

## Open ACF
    my $acf = new AFX::ACF();

## If updated failed
    if ( !$updated ) {

## Print failed update message
        message_failed_update($acf);
    }

## Print config started message
    config_started($acf);

## Print version message
    print $acf->message_title() . "\n\n";

## Check if keyfile exists and encryption works
    if ( $acf->open_keyfile() ) {

## Encrypt non-encrypted passwords
        $acf->encrypt_passwd();

## Update ACF
        $acf->update();
    }

## Check is site's password encrypted
    if ( !$acf->check_site_passwd() ) {

## Print Empty password found
        $acf->message_empty_passwd();

## Set Error status
        my $status = 1;

## Print Configure finished message
        config_finished( $acf, $status );

## Exit with status
        exit($status);
    }

## Check current Site's Identifier
    my $site_id = $acf->open_sitefile();

## Set site_id as TEST if not specified
    $site_id = 'TEST' if !$site_id;

## Print ASF saving confirm
    print "\nPLEASE ENTER DEFAULT SITE NAME[$site_id]: ";

## Require user input to continue
    chomp( my $new_site_id = uc(<STDIN>) );

## If Site Identifier specified correctly
    if ( $new_site_id and $new_site_id ne $site_id ) {

## Set sitefile
        $acf->set_sitefile($new_site_id);

## Set site_id
        $site_id = $new_site_id;
    }

## Open default ASF
    my $asf = new AFX::ASF( $acf, $site_id );

## If ASF not opened
    if ( !$asf ) {

## Set Error status
        my $status = 1;

## Print Configure finished message
        config_finished( $acf, $status );

## Exit with status
        exit($status);
    }

## Add Master File
    $asf->add_master($site_id) if $site_id;

## Set default site_mach
    my $site_mach = 'LNX';

## Get default machine for specified
    my $machine_default = $acf->get_machine($site_mach);

## Check default machine exist in ACF
    if ( !$machine_default ) {

## Print Not supported unit
        $acf->message_failed_unit($site_mach);

## Set Error status
        my $status = 1;

## Print Configure finished message
        config_finished( $acf, $status );

## Exit with status
        exit($status);
    }

## Add machine into default ASF
    my $machine = $asf->add_machine( $machine_default, $site_mach, $site_mach );

## Try to use Process library
    eval "use AFX::Process";

## Check AFX::Process library
    if ($@) {

## Print failed loading AFX::Process library
        $acf->message_failed_library("AFX::Process");

## Set Error status
        my $status = 1;

## Print Config finished message
        config_finished( $acf, $status );

## Exit with status
        exit($status);

    }

## Create Process
    my $process = new AFX::Process( $acf, $asf, 'config' );

## If process wasn't created
    if ( !$process ) {

## Print process failed message
        $acf->message_failed_process();

## Set Error status
        my $status = 1;

## Print Config finished message
        config_finished( $acf, $status );

## Exit with status
        exit($status);
    }

## Print ATF generate message
    print message_generate($acf);

## Create new ARF
    my $arf = new AFX::ARF( $process, $machine, 'audit' );

## Get directory
    my $atfdir = $acf->get_directory('template');

## If machines exist in ACF
    if ( $acf->get_list_machine() ) {

## Check machines in ACF
        foreach my $machine ( @{ $acf->get_list_machine() } ) {

## Open graphviz
            if ( open( GV, "> $atfdir/$machine->{unit}.gv" ) ) {

## Print ATF relations
                print GV "digraph $machine->{unit} {\nsplines=false;\n";
                close(GV);
            }
        }
    }

## Set Ok status
    my $status = 0;

## Find ATF
    my $atfnames = $acf->find_atf( 'template', "" );

## Check if templates exist
    if ($atfnames) {

## Check templates
        foreach my $atfname ( @{$atfnames} ) {

## Open ATF
            my $atf = new AFX::ATF( $arf, $atfname, 'template' );

## If ATF invalid
            if ( !$atf ) {

## Set error status
                $status++;

## Goto next template
                next;
            }

## Generate HTML description
            $atf->generate();
        }
    }

## Find STF
    my $stfnames = $acf->find_atf( 'scenario', "" );

## Check if scenario exist
    if ($stfnames) {

## Check templates
        foreach my $stfname ( @{$stfnames} ) {

## Open ATF
            my $stf = new AFX::ATF( $arf, $stfname, 'scenario' );

## if STF invalid
            if ( !$stf ) {

## Set error status
                $status++;

## Goto next template
                next;
            }

## Generate HTML description
            $stf->generate();
        }
    }

## If machines exist in ACF
    if ( $acf->get_list_machine() ) {

## Check machines in ACF
        foreach my $machine ( @{ $acf->get_list_machine() } ) {

## Open graphviz
            if ( open( GV, ">> $atfdir/$machine->{unit}.gv" ) ) {

## Print ATF relations
                print GV "}";
                close(GV);
            }
        }
    }

## Print config finished message
    config_finished( $acf, $status );

} ## end sub config

##############################################################################
#
# config_started()
#
# Message: Config started
#
# Return:
# - logged message
#
# Arguments expected are:
# - acf      - ACF Object

sub config_started {
    my ($acf) = @_;

## Get time
    my $now = localtime;

## Set message
    my $message = "\n## Configuration started at $now ";
    $message .= "#" while ( length($message) < 70 );
    $message .= "\n";

## Return message
    return $acf->logging( $message, 1, 'audit' );

} ## end sub config_started

##############################################################################
#
# config_finished()
#
# Message: Config finished
#
# Return:
# - logged message
#
# Arguments expected are:
# - acf      - ACF Object
# - status   - Process status

sub config_finished {
    my ( $acf, $status ) = @_;

## Get time
    my $now = localtime;

## Set message
    my $message = "\n## Configuration finished at $now ";
    $message .= "#" while ( length($message) < 70 );
    $message .= "\n";

## Show Status
    $message .= "STATUS=$status\n";

## If status is failed
    if ($status) {
        $message .= "INFO=Failed parameters or processing errors exist\n";
    }
    else {
        $message .= "INFO=No issues detected during verification\n";
    }

## Return message
    return $acf->logging( $message, 1, 'audit' );

} ## end sub config_finished

##############################################################################
#
# config_usage()
#
# Message: Config usage
#
# Return:
# - logged message

sub config_usage {

## Set message
    my $message = "Usage: [perl] config.pl";
    $message .= "\nOS: $Config{archname} ($Config{osvers})\n";

## Print gnulibc version
    $message .= "Gnulibc: $Config{gnulibc_version}\n"
      if $Config{gnulibc_version};

## Return message
    return $message;

} ## end sub config_usage

##############################################################################
#
# lib_update()
#
# Update some platform dependent libraries
#
# Return:
# - 1          - if updated successfully
# - 0          - otherwise

sub lib_update {

## Define library's directories
    my $threads_dir = 'lib/auto/threads/';
    my $expat_dir   = 'lib/auto/XML/Parser/Expat/';
    my $tty_dir     = 'lib/auto/IO/Tty/';
    my $sax_dir     = 'lib/XML/SAX/';

## Define libraries
    my $threads = $threads_dir . 'threads.so';
    my $expat   = $expat_dir . 'Expat.so';
    my $tty     = $tty_dir . 'Tty.so';

## Define libraries
    my $threads_dll = $threads_dir . 'threads.dll';
    my $expat_dll   = $expat_dir . 'Expat.dll';
    my $tty_dll     = $tty_dir . 'Tty.dll';

## Define SAX Parser.ini
    my $parser    = $sax_dir . 'ParserDetails.ini';
    my $parser_ex = $sax_dir . 'ParserDetails-expat.ini';

## Delete threads library
    unlink($threads) if $threads and -d $threads_dir and -f $threads;
    unlink($threads_dll)
      if $threads_dll
          and -d $threads_dir
          and -f $threads_dll;

## Delete expat library
    unlink($expat)     if $expat     and -d $expat_dir and -f $expat;
    unlink($expat_dll) if $expat_dll and -d $expat_dir and -f $expat_dll;

## Delete expat library
    unlink($tty)     if $tty     and -d $tty_dir and -f $tty;
    unlink($tty_dll) if $tty_dll and -d $tty_dir and -f $tty_dll;

## Define MACH's libraries
    my ( $threads_mach, $expat_mach, $tty_mach, $threads_mach_dll,
        $expat_mach_dll, $tty_mach_dll );

    if ( $Config{osname} =~ /linux/i and $Config{gnulibc_version} =~ /2.(\d+)/ )
    {

## Define libraries for Linux with glibc2_X
        $threads_mach = $threads_dir . "threads-lnx2_$1.so";
        $expat_mach   = $expat_dir . "Expat-lnx2_$1.so";
        $tty_mach     = $tty_dir . "Tty-lnx2_$1.so";
    }
    elsif ( $^O eq 'MSWin32' ) {

## Define libraries for Windows
        $threads_mach_dll = $threads_dir . 'threads-win.dll';
        $expat_mach_dll   = $expat_dir . 'Expat-win.dll';
    }

    elsif ( $Config{osname} =~ /cygwin/i and $Config{osvers} =~ /^1.(\d+)/ ) {

## Define libraries for Cygwin 1.5
        $threads_mach_dll = $threads_dir . "threads-cygwin1_$1.dll";
        $expat_mach_dll   = $expat_dir . "Expat-cygwin1_$1.dll";
        $tty_mach_dll     = $tty_dir . "Tty-cygwin1_$1.dll";
    }
    elsif ( $Config{osname} =~ /aix/i ) {

## Define libraries for AIX
        $threads_mach = $threads_dir . 'threads-aix.so';
        $expat_mach   = $expat_dir . 'Expat-aix.so';
        $tty_mach     = $tty_dir . 'Tty-aix.so';
    }

## Copy files
    copy( $threads_mach, $threads )
      if $threads_mach
          and -d $threads_dir
          and !-f $threads;
    copy( $expat_mach, $expat ) if $expat_mach and -d $expat_dir and !-f $expat;
    copy( $tty_mach,   $tty )   if $tty_mach   and -d $tty_dir   and !-f $tty;

## Check DLL Threads library
    if ( $threads_mach_dll and -d $threads_dir and !-f $threads_dll ) {
        copy( $threads_mach_dll, $threads_dll );
        chmod( '0755', $threads_dll );
    }

## Check DLL Expat library
    if ( $expat_mach_dll and -d $expat_dir and !-f $expat_dll ) {
        copy( $expat_mach_dll, $expat_dll );
        chmod( '0755', $expat_dll );
    }

## Check DLL Tty library
    if ( $tty_mach_dll and -d $tty_dir and !-f $tty_dll ) {
        copy( $tty_mach_dll, $tty_dll );
        chmod( '0755', $tty_dll );
    }

## Try to use Threads library
    eval "use threads 1.39;";

## If error occured
    if ($@) {

## Print error message
        print "Failed loading Threads library. Error: $@\n";

## Return Error
        return 0;
    }

## Try to use Tty library
    eval "use IO::Tty;";

## If error occured
    if ( $@ and $^O ne 'MSWin32' ) {

## Print error message
        print "Warning: Failed loading IO::Tty library. Error: $@\n";

## Return Error
        return 0;
    }

## Try to use Expat parser as SAX
    eval "use XML::Parser::Expat;";

## If Expat library available use it
    if ( !$@ ) {

## Replace ini-file with expat
        copy( $parser_ex, $parser ) if $parser_ex and -d $sax_dir;
    }
    else {

## Print error message
        print "Warning: Failed loading Expat library. Error: $@\n";
    }

## Return Ok
    return 1;

} ## end sub lib_update

##############################################################################
#
# message_failed_update()
#
# Message: Libraries failed update.
#
# Return:
# - logged message
#
# Arguments expected are:
# - acf      - ACF Object

sub message_failed_update {
    my ($acf) = @_;

## Set message
    my $message = "Error: Libraries update failed. OS isn't supported.";

## Return message
    return $acf->logging( $message, 1, 'audit' );

} ## end sub message_failed_update

##############################################################################
#
# message_generate()
#
# Message: Please wait while ATF/STF will be checked
#
# Return:
# - logged message
#
# Arguments expected are:
# - acf      - ACF Object

sub message_generate {
    my ($acf) = @_;

## Set message
    my $message =
      "\nChecking ATF/STF. Generating html description files. Please wait.";
    $message .=
      "\nDescription files will be located within template/scenario folders.\n";

## Return message
    return $message;

} ## end sub message_generate
